#!/usr/bin/env python
###
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
###

try:
 	import pygtk
  	pygtk.require("2.0")
except:
  	pass
try:
    import gtk
    from gtk import gdk
    import gtk.glade
    import gobject
    import cairo
    import shapelib
except:
	sys.exit(1)

import os

COLUMN_NUMBER = 0
COLUMN_STRING = 1


class Gui:

    def __init__(self):
        self.zoom = 1
        self.zoom_coeff = 1
        self.vertices = None
        self.type = 0
        self.bg_offsets = None
        #Set the Glade file
        self.gladefile = "gui/gui.glade"  
        self.wTree = gtk.glade.XML(self.gladefile)
        #Get the Main Window, and connect the "destroy" event
        self.window = self.wTree.get_widget("window1")
        if (self.window):
            self.window.connect("destroy", self.destroy)        
        self.screen = Screen(*self.open_shape('eestimaa.shp'))
        self.screen.set_size_request(int(self.deltax), int(self.deltay))
        self.scrolledwindow = self.wTree.get_widget("scrolledwindow1")
        self.scrollbarZoom = self.wTree.get_widget("vscrollbarZoom")

        #self.vertices = self.open_shape('eestimaa.shp')
        #self.vwidget.show_all()
        #self.box.pack_start(self.screen)
        #self.screen.set_size_request(int(self.deltax), int(self.deltay))
        self.scrolledwindow.add_with_viewport(self.screen)
        self.window.show()
        
        dic = {
            "on_open_activate" : self.on_open,
            "on_quit_activate" : gtk.main_quit,
            "on_vscrollbarZoom_value_changed": self.on_zoom,
            "on_scrolledwindow1_scroll_child": self.scrolled
            }
        
        self.wTree.signal_autoconnect(dic)
        #self.add_columns()
        self.window.show_all()
        gtk.gdk.notify_startup_complete()

    def calc_zoom(self):
        pass
        '''
        x,y,w,h = self.box.get_allocation()
        ww,hh = self.screen.size_request()
        print x, w, y ,h
        if w>h and ww > hh:
            zoom = ww/(w-x)
        else:
            zoom = hh/h
        self.set_zoom(zoom)
        self.zoom_coeff = ((zoom-zoom%50)+100) / 1000.0
        #self.scrollbarZoom.set_range(0, (zoom - zoom%50)+100)
        self.scrollbarZoom.set_value(zoom%1000)
        '''
    def scrolled(self,type,horz):
        print type, horz
    def on_open(self,w):
        f = gtk.FileChooserDialog("Open Shapefile", self.window, gtk.FILE_CHOOSER_ACTION_OPEN,
            buttons = ( gtk.STOCK_CANCEL,gtk.RESPONSE_CLOSE,
                        gtk.STOCK_OPEN,gtk.RESPONSE_OK))
        filter = gtk.FileFilter()
        filter.set_name("Shapefile")
        #filter.add_mime_type("audio/x-mpegurl")
        filter.add_pattern("*.shp")
        f.add_filter(filter)

        f.connect("response",self.dialog_response)
        f.show_all()
    
    def dialog_response(self,w,s):
        if s == gtk.RESPONSE_OK:
            #if w.get_filter().get_name() == "M3U Playlist":
            self.screen.set_verts(*self.open_shape(w.get_filename()))
            #self.vertices, self.type = self.open_shape(w.get_filename())
            self.screen.set_size_request(int(self.deltax), int(self.deltay))
            self.calc_zoom()
            #self.draw()
        w.destroy()
    
    def open_shape(self, filename):
        vertices = []
        sh = shapelib.open(filename, 'rb')
        ent = sh.info()[0]
        types = sh.info()[1]
        emin = sh.info()[2]
        emax = sh.info()[3]
        if self.bg_offsets == None:
            self.bg_offsets = (emin[0]-100000, emin[1]-100000,emax[0]+100000, emax[1]+100000,)
            self.deltax, self.deltay = self.bg_offsets[2] - self.bg_offsets[0], self.bg_offsets[3] - self.bg_offsets[1]
        #self.deltax, self.deltay = emax[0] - emin[0], emax[1] - emin[1]
            
        print ent, types
        for i in range(0, ent):
            verts = sh.read_object(i)
            #print verts.type, len(verts.vertices()[0])
            ext = verts.extents()[0]
            #self.vertices.append( [(x[0] - ext[0], x[1]-ext[1],) for x in verts.vertices()[0]] )
            #vert = [(x[0] - emin[0], emax[1]- x[1],) for x in verts.vertices()[0]] 
            vert = [(x[0] - self.bg_offsets[0], self.bg_offsets[3]- x[1],) for x in verts.vertices()[0]] 
            #print vert
            #if verts.type != 5:
            #    print verts.type,
            vertices.append( vert )
        
        sh.close()
        return vertices, types

    def on_zoom(self,w):
        #print w.get_value()
        self.set_zoom(w.get_value() * self.zoom_coeff)

    def set_zoom(self, zoom):
        if zoom == 0: zoom=1
        self.zoom = zoom
        hadj0_val = self.scrolledwindow.get_hadjustment().get_value()
        hadj0_upper = self.scrolledwindow.get_hadjustment().get_upper()
        vadj0_val = self.scrolledwindow.get_vadjustment().get_value()
        vadj0_upper = self.scrolledwindow.get_vadjustment().get_upper()
        
        self.screen.set_zoom(zoom)
        self.screen.set_size_request(self.deltax / zoom, self.deltay / zoom)
        hadj1_val = self.scrolledwindow.get_hadjustment().get_value()
        hadj1_upper = self.scrolledwindow.get_hadjustment().get_upper()
        vadj1_val = self.scrolledwindow.get_vadjustment().get_value()
        vadj1_upper = self.scrolledwindow.get_vadjustment().get_upper()
        
        hadj = self.scrolledwindow.get_hadjustment()
        vadj = self.scrolledwindow.get_vadjustment()
        #hjust=hadj1.get_value() + (hadj0.get_upper() - hadj1.get_upper()) /2.0
        hjust= (hadj0_val / hadj0_upper) * hadj1_upper
        vjust= (vadj0_val / vadj0_upper) * vadj1_upper
        print hadj0_upper, hadj1_upper
        hadj.set_value(hjust)
        vadj.set_value(vjust)
        
        self.scrolledwindow.set_hadjustment(hadj)
        self.scrolledwindow.set_vadjustment(vadj)
        #,hadj1, vadj0,vadj1
        #self.draw()

    def get_zoom(self):
        return self.zoom
        
    def get_zoomed_deltas(self):
        return self.deltax/self.zoom, self.deltay/self.zoom
        
    def destroy(klass, widget=None):
        gtk.main_quit()
        

    def draw_pixmap(self):
        width, height = self.get_zoomed_deltas()
        pixmap = gtk.gdk.Pixmap(self.screen.window, width % 8192, height % 8192)
        #cr = self.screen.window.cairo_create()
        cr = pixmap.cairo_create()
        cr.set_source_rgba(0,0,0,1)
        cr.set_line_width(1.0)
        #if True or self.repaint:
        #if self.type == shapelib.SHPT_POLYGON:
        for x in self.vertices:
            cr.new_path()
            cr.move_to(*(i / self.zoom for i in x[0]))
            for y in x:
                if self.zoom == 0:
                    cr.line_to(*y)
                else:
                    cr.line_to(*(i / self.zoom for i in y))
            cr.stroke()
        self.screen.set_pixmap(pixmap)
        
        '''
        if self.type == shapelib.SHPT_ARC:
            for x in self.vertices:
                cr.new_path()
                cr.move_to(*(i / self.zoom for i in x[0]))
                for y in x:
                    if self.zoom == 0:
                        cr.line_to(*y)
                    else:
                        cr.line_to(*(i / self.zoom for i in y))
                cr.stroke()
        '''
        
        
        
# Create a GTK+ widget on which we will draw using Cairo
class Screen(gtk.DrawingArea):

    # Draw in response to an expose-event
    __gsignals__ = { "expose-event": "override" }

    def __init__(self, verts=[], type=0):
        super(Screen,self).__init__()
        # gtk.Widget signals
        self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white"))
        self.connect("button_press_event", self.button_press)
        self.connect("button_release_event", self.button_release)
        self.connect("motion_notify_event", self.motion_notify)
        self.connect("configure_event", self.configure) #resize
        self.connect("realize", self.realize)
        # More GTK voodoo : unmask events
        self.add_events(gdk.BUTTON_PRESS_MASK |
                        gdk.BUTTON_RELEASE_MASK |
                        gdk.POINTER_MOTION_MASK)
        self.cr = None
        self.zoom = 1
        self.bg_vertices = verts
        print verts
        self.vertices = []
        self.type = 0
        self.repaint = True
        self.pixmap = None
        self.do_repaint()
        self.path = None
        self.bg_path = None

    def set_verts(self, verts, type):
        self.vertices = verts
        self.type = type
        self.path = None
        self.queue_draw()
        #self.set_zoom(1)
        
    def set_pixmap(self, pix):
        self.pixmap = pix
        
    def do_repaint(self):
        self.repaint = True
        self.queue_draw()

    def set_zoom(self, val):
        self.zoom = val
        #if self.zoom == 0: self.zoom = 1
        self.do_repaint()
        
    # Handle the expose-event by drawing
    def do_expose_event(self, event):
        #x , y, width, height = event.area
        #if self.pixmap:
        #    self.window.draw_drawable(self.get_style().fg_gc[gtk.STATE_NORMAL],
        #        self.pixmap, x, y, x, y, width, height)
        #return True
        # Create the cairo context
        self.cr = self.window.cairo_create()
        self.hitpath = None #Is set later

        # Restrict Cairo to the exposed area; avoid extra work
        self.cr.rectangle(event.area.x, event.area.y,
                event.area.width, event.area.height)
        self.cr.clip()

        self.draw(self.cr, *self.window.get_size())

    def button_press(self,widget,event):
        pass
    def button_release(self,widget,event):
        pass
    def motion_notify(self,widget,event):
        pass
    def configure(self,widget,event):
        pass
    def realize(self,widget):
        pass

    def draw(self,cr, width, height):
        cr = self.window.cairo_create()
        cr.new_path()
        cr.set_source_rgba(0,0,0,1)
        cr.set_line_width(1.0*self.zoom)
        cr.set_operator(cairo.OPERATOR_OVER)
        #if True or self.repaint:
        #cr.transform(cairo.Matrix(1.0/self.zoom,0,0,1.0/self.zoom,0,0))
        cr.scale(1.0/self.zoom,1.0/self.zoom)
        #cr.set_line_width(100)
        cr.move_to(5,5)
        cr.line_to(50,100)
        cr.line_to(150,150)
        cr.close_path()
        cr.stroke()
        for x in self.bg_vertices:
            #cr.new_path()
            #cr.move_to(*(i / self.zoom for i in x[0]))
            cr.move_to(*x[0])
            for y in x:
                cr.line_to(*y)
            cr.close_path()
            #    if self.zoom == 0:
            #        cr.line_to(*y)
            #    else:
            #        cr.line_to(*(i / self.zoom for i in y))
        #print self.zoom
        #cr.transform(cairo.Matrix(1.0/self.zoom,0,0,1.0/self.zoom,0,0))
        #cr.close_path()
        cr.stroke()
        cr.set_source_rgba(1,0,0,0.8)
        cr.new_path()
        #cr.scale(1.0/self.zoom,1.0/self.zoom)
        #if self.type == shapelib.SHPT_POLYGON:
        if self.path:
            #print "append"
            cr.append_path(self.path)
            cr.stroke()
        elif self.vertices:
            cr.new_path()
            for x in self.vertices:
                #cr.new_path()
                #cr.move_to(*(i / self.zoom for i in x[0]))
                cr.move_to(*x[0])
                for y in x:
                    cr.line_to(*y)
                    #if self.zoom == 0:
                    #    cr.line_to(*y)
                    #else:
                    #    #cr.line_to(*(i / self.zoom for i in y))
                        
            #cr.stroke()
            
            print "Flat path"
            self.path = cr.copy_path_flat()
            cr.stroke()
            #print self.path.path_destroy
        
        '''
        if self.type == shapelib.SHPT_ARC:
            for x in self.vertices:
                cr.new_path()
                cr.move_to(*(i / self.zoom for i in x[0]))
                for y in x:
                    if self.zoom == 0:
                        cr.line_to(*y)
                    else:
                        cr.line_to(*(i / self.zoom for i in y))
                cr.stroke()
        '''
        #define SHPT_POINT		1	Points
        #define SHPT_ARC		3	Arcs (Polylines, possible in parts)
        #define SHPT_POLYGON		5	Polygons (possible in parts)
        #define SHPT_MULTIPOINT	8	MultiPoint (related points)

        #self.repaint = False
        #cr.rectangle(0,0, 25,25)
        #cr.fill()

#from __future__ import with_statement
import sys,linecache
def traceit(frame, event, arg):
    if event == "line":
        lineno = frame.f_lineno
        filename = frame.f_globals["__file__"]
        if filename == "<stdin>":
            filename = "glade.py"
        if (filename.endswith(".pyc") or
            filename.endswith(".pyo")):
            filename = filename[:-1]
        name = frame.f_globals["__name__"]
        line = linecache.getline(filename, lineno)
        print "%s:%s: %s" % (name, lineno, line.rstrip())

    return traceit
    
if __name__ == "__main__":
    #sys.settrace(traceit)
    g = Gui()
    gtk.gdk.threads_init()
    gtk.main()
